/**************************************************************************
Copyright (C) 2008-2010 United States Government. All rights reserved. 

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
****************************************************************************/
package rage 

import grails.test.*
import groovy.util.slurpersupport.GPathResult
import java.awt.geom.Point2D

class FeedViewKmlTests extends GroovyPagesTestCase {

	def gspTemplate = null;
	String rgbColor = "ffff00";
	String bgrColor = "00ffff";

	protected void setUp() {
		super.setUp();

		def targetGspFile = "grails-app/views/feed/kml.gsp";
		def kmlGsp = new File(targetGspFile);
		assertNotNull('Unable to load target gsp file: ${targetGspFile}', kmlGsp);
		def fileSize = kmlGsp.text.size();
		assertTrue(fileSize > 0);
		gspTemplate = kmlGsp.text;
	}

	protected void tearDown() {
		super.tearDown();
	}

	void testKml_noPoints() {
		assertNotNull("Unable to test GSP, template is null!", gspTemplate);

		def feed = createFeed("title", "test feed", "name", "test placemark", null, null, null);
		def result = applyTemplate(gspTemplate, [feedInstance: feed, feedColor: rgbColor]);
		assertNotNull("GSP provided no output", result);

		def kml = new XmlSlurper().parseText(result);

		assertDocument(kml);
		assertStyle(kml.Document, bgrColor, bgrColor);
		assertPlacemark(kml.Document, "#" + bgrColor, null);
		assertSnippet(kml.Document.Placemark);
	}

	void testKml_singlePoint_with_timestamp() {
		assertNotNull("Unable to test GSP, template is null!", gspTemplate)
		
		String timestamp = "2001-01-05T00:00:00"
		Double lat = new Double(10.0)
		Double lon = new Double(15.0)
		Point2D point = new Point2D.Double(lon, lat)
		List<Point2D> points = [point]
		def feed = createFeed("title", "test feed", "name", "test placemark", timestamp, points, Geometry.Type.POINT)
		def result = applyTemplate(gspTemplate, [feedInstance: feed, feedColor: rgbColor])
		assertNotNull("GSP provided no output", result)

		def kml = new XmlSlurper().parseText(result)

		assertDocument(kml)
		assertStyle(kml.Document, bgrColor, bgrColor)
		assertPlacemark(kml.Document, "#" + bgrColor, timestamp)
		assertPoint(kml.Document.Placemark, "${lon},${lat},0")
	}

	void testKml_multi_points() {
		assertNotNull("Unable to test GSP, template is null!", gspTemplate)

		//Create box
		Double lat1 = new Double(10.0)
		Double lon1 = new Double(15.0)
		Double lat2 = new Double(10.0)
		Double lon2 = new Double(20.0)
		Double lat3 = new Double(5.0)
		Double lon3 = new Double(20.0)
		Double lat4 = new Double(5.0)
		Double lon4 = new Double(15.0)
		Point2D point1 = new Point2D.Double(lon1, lat1)
		Point2D point2 = new Point2D.Double(lon2, lat2)
		Point2D point3 = new Point2D.Double(lon3, lat3)
		Point2D point4 = new Point2D.Double(lon4, lat4)
		List<Point2D> points = [point1, point2, point3, point4]
		def feed = createFeed("title", "test feed", "name", "test placemark", null, points, Geometry.Type.POINT)
		def result = applyTemplate(gspTemplate, [feedInstance: feed, feedColor: rgbColor])
		assertNotNull("GSP provided no output", result)
		println("${result}")

		def kml = new XmlSlurper().parseText(result)

		assertDocument(kml)
		assertStyle(kml.Document, bgrColor, bgrColor)
		assertPlacemark(kml.Document, "#" + bgrColor, null)
		List<String> coords = ["${lon1},${lat1},0", "${lon2},${lat2},0", "${lon3},${lat3},0", "${lon4},${lat4},0"]
		assertMultiGeometry_points(kml.Document.Placemark, coords)
	}

	void testKml_Polygon() {
		assertNotNull("Unable to test GSP, template is null!", gspTemplate)

		//Create box
		Double lat1 = new Double(10.0)
		Double lon1 = new Double(15.0)
		Double lat2 = new Double(10.0)
		Double lon2 = new Double(20.0)
		Double lat3 = new Double(5.0)
		Double lon3 = new Double(20.0)
		Double lat4 = new Double(5.0)
		Double lon4 = new Double(15.0)
		Point2D point1 = new Point2D.Double(lon1, lat1)
		Point2D point2 = new Point2D.Double(lon2, lat2)
		Point2D point3 = new Point2D.Double(lon3, lat3)
		Point2D point4 = new Point2D.Double(lon4, lat4)
		List<Point2D> points = [point1, point2, point3, point4]
		def feed = createFeed("title", "test feed", "name", "test placemark", null, points, Geometry.Type.POLYGON)
		def result = applyTemplate(gspTemplate, [feedInstance: feed, feedColor: rgbColor])
		assertNotNull("GSP provided no output", result)
		//println("${result}")

		def kml = new XmlSlurper().parseText(result)

		assertDocument(kml)
		assertStyle(kml.Document, bgrColor, bgrColor)
		assertPlacemark(kml.Document, "#" + bgrColor, null)
		assertMultiGeometry_polygon(kml.Document.Placemark, 
				"${lon1},${lat1},0", //point coords
				"${lon1},${lat1},0${lon2},${lat2},0${lon3},${lat3},0${lon4},${lat4},0") //poly coords
	}

	private Feed createFeed(String feedTitle,
			String feedDescr,
			String placemarkName,
			String placemarkDesc,
			String placemarkTimestamp,
			List<Point2D> points,
			Geometry.Type type) {

		def placemark = new Placemark()
		placemark.name = placemarkName
		placemark.description = placemarkDesc
		placemark.timestamp = placemarkTimestamp
		placemark.geometry = new Geometry(points:points, type:type)
		List<Placemark> placemarks = [placemark]
		
		def feed = new Feed()
		feed.title = feedTitle
		feed.description = feedDescr
		feed.placemarks = placemarks

		return feed		
	}

	private assertSnippet(GPathResult node) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'Snippet'",
			1, node.Snippet.size())
	}

	private assertDocument(GPathResult node) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'Document'",
			1, node.Document.size()) 
	}

	private assertPlacemark(GPathResult node, String expectedStyleUrl, String expectedTimestamp) {
		assertEquals("Node, ${node.name()}, does not contain expected element(s) 'Placemark'",
			1, node.Placemark.size())

		def placemarkNode = node.Placemark
		assertEquals("Node, ${placemarkNode.name()}, does not contain expected element(s) 'description'",
			1, placemarkNode.description.size())
		assertEquals("Node, ${placemarkNode.name()}, does not contain expected element(s) 'styleUrl'",
			1, placemarkNode.styleUrl.size())
		assertEquals("Unexpected value for element 'styleUrl'", 
			expectedStyleUrl, (String) placemarkNode.styleUrl.text())

		if (expectedTimestamp) {
			assertTimestamp(placemarkNode, expectedTimestamp);
		}
		
	}

	private assertTimestamp(GPathResult node, String expectedTimestamp) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'TimeStamp'",
			1, node.TimeStamp.size())

		def timestampNode = node.TimeStamp
		assertEquals("Node, ${timestampNode.name()}, does not contain expected element 'when'",
			1, timestampNode.when.size())
		assertEquals("Unexpected value for element 'when'", 
			expectedTimestamp, (String) timestampNode.when.text())
	}

	private assertPoint(GPathResult node, String expectedCoords) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'Point'",
			1, node.Point.size())

		def pointNode = node.Point
		assertEquals("node, ${pointNode.name()}, does not contain expected element 'coordinates'",
			1, pointNode.coordinates.size())
		String coords = node.Point.coordinates
		assertCoords(coords, expectedCoords)
	}

	private assertPolygon(GPathResult node, String expectedCoords) {
		assertEquals("node, ${node.name()}, does not contain expected element 'Polygon'",
			1, node.Polygon.size())
		
		def polygonNode = node.Polygon
		assertEquals("node, ${polygonNode.name()}, does not contain expected element 'coordinates'",
			1, polygonNode.outerBoundaryIs.LinearRing.coordinates.size())

		String coords = polygonNode.outerBoundaryIs.LinearRing.coordinates
		assertCoords(coords, expectedCoords)
	}

	private assertCoords(String coordsValue, String expectedCoords) {
		coordsValue = coordsValue.trim()
		coordsValue = coordsValue.replace("\n", "")
		coordsValue = coordsValue.replace(" ", "")
		coordsValue = coordsValue.replace("\t", "")
		assertEquals("Unexpected coordinates value",
			expectedCoords, coordsValue)
	}

	private assertMultiGeometry_polygon(GPathResult node, String expectedPointCoords, String expectedPolyCoords) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'MultiGeometry'",
			1, node.MultiGeometry.size())

		def mulitGeoNode = node.MultiGeometry
		assertPoint(mulitGeoNode, expectedPointCoords)
		assertPolygon(mulitGeoNode, expectedPolyCoords)
	}

	private assertMultiGeometry_points(GPathResult node, List<String> expectedCoordinates) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'MultiGeometry'",
			1, node.MultiGeometry.size())

		def multiGeoNode = node.MultiGeometry
		assertEquals("Unexpected number of 'Point elements contained in MultiGeometry",
			expectedCoordinates.size(), multiGeoNode.Point.size())

		multiGeoNode.Point.eachWithIndex{pointNode, i->
			assertEquals("node, ${pointNode.name()}, does not contain expected element 'coordinates'",
				1, pointNode.coordinates.size())
			String coords = pointNode.coordinates
			println("coords: " + coords)
			assertCoords(coords, expectedCoordinates.get(i))
		}
	}

	private assertStyle(GPathResult node, String expectedStyleId, String expectedColor) {
		assertEquals("Node, ${node.name()}, does not contain expected element 'Style'",
			1, node.Style.size())
		def styleNode = node.Style

		assertEquals("Node, ${styleNode.name()}, does not contain expected value for attribute: 'id'",
			expectedStyleId, (String) styleNode.@id)

		assertEquals("Node, ${styleNode.name()}, does not contain expected element 'IconStyle'",
			1, styleNode.IconStyle.size())
		assertTrue("IconStyle color does not contain expected color: ${expectedColor}",
			 ((String)styleNode.IconStyle.color.text()).contains(expectedColor))

		assertEquals("Node, ${styleNode.name()}, does not contain expected element 'LineStyle'",
			1, styleNode.LineStyle.size())
		assertTrue("LineStyle color does not contain expected color: ${expectedColor}",
			 ((String)styleNode.LineStyle.color.text()).contains(expectedColor))

		assertEquals("Node, ${styleNode.name()}, does not contain expected element 'PolyStyle'",
			1, styleNode.PolyStyle.size())
		assertTrue("PolyStyle color does not contain expected color: ${expectedColor}",
			 ((String)styleNode.PolyStyle.color.text()).contains(expectedColor))
	}
}